﻿namespace Catel.Articles._08___WP7_Camera.ViewModels
{
    using System.Collections.Generic;
    using System.Windows.Media.Imaging;
    using Catel.Data;
    using MVVM;
    using MVVM.Services;
    using Data;
    using Microsoft.Phone.Controls;

    /// <summary>
    /// Main page view model.
    /// </summary>
    public class MainPageViewModel : ViewModelBase
    {
        #region Variables
        #endregion

        #region Constructor & destructor
        /// <summary>
        /// Initializes a new instance of the <see cref="MainPageViewModel"/> class.
        /// </summary>
        public MainPageViewModel()
            : base()
        {
            Flick = new Command<FlickData>(OnFlickExecute, OnFlickCanExecute);
            TakePhoto = new Command(OnTakePhotoExecute, OnTakePhotoCanExecute);
            View = new Command(OnViewExecute, OnViewCanExecute);
            About = new Command(OnAboutExecute);

            CameraService.CaptureThumbnailAvailable += OnCameraServiceCaptureThumbnailAvailable;
            CameraService.CaptureImageAvailable += OnCameraServiceCaptureImageAvailable;
            CameraService.Start();
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets the title of the view model.
        /// </summary>
        /// <value>The title.</value>
        public override string Title { get { return "Camera service"; } }

        /// <summary>
        /// Gets the photo repository.
        /// </summary>
        /// <value>The photo repository.</value>
        private IPhotoRepository PhotoRepository { get { return GetService<IPhotoRepository>(); } }

        /// <summary>
        /// Gets the camera service.
        /// </summary>
        /// <value>The camera service.</value>
        private ICameraService CameraService { get { return GetService<ICameraService>(); } }

        /// <summary>
        /// Gets or sets a value indicating whether the view model is currently taking a photo.
        /// </summary>
        public bool IsTakingPhoto
        {
            get { return GetValue<bool>(IsTakingPhotoProperty); }
            set { SetValue(IsTakingPhotoProperty, value); }
        }

        /// <summary>
        /// Register the IsTakingPhoto property so it is known in the class.
        /// </summary>
        public static readonly PropertyData IsTakingPhotoProperty = RegisterProperty("IsTakingPhoto", typeof(bool));

        /// <summary>
        /// Gets or sets the current photo.
        /// </summary>
        public BitmapImage CurrentPhoto
        {
            get { return GetValue<BitmapImage>(CurrentPhotoProperty); }
            set { SetValue(CurrentPhotoProperty, value); }
        }

        /// <summary>
        /// Register the CurrentPhoto property so it is known in the class.
        /// </summary>
        public static readonly PropertyData CurrentPhotoProperty = RegisterProperty("CurrentPhoto", typeof(BitmapImage));
        #endregion

        #region Commands
        /// <summary>
        /// Gets the Flick command.
        /// </summary>
        public Command<FlickData> Flick { get; private set; }

        /// <summary>
        /// Method to invoke when the Flick command can execute state is retrieved.
        /// </summary>
        /// <param name="parameter">The parameter.</param>
        /// <returns><c>true</c> if the command can be executed; otherwise <c>false</c>.</returns>
        private bool OnFlickCanExecute(FlickData parameter)
        {
            if (parameter == null)
            {
                return true;
            }

            if (parameter.HorizontalVelocity < 0)
            {
                if (PhotoRepository.Photos.Count > 0)
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Method to invoke when the Flick command is executed.
        /// </summary>
        /// <param name="parameter">The parameter.</param>
        private void OnFlickExecute(FlickData parameter)
        {
            var navigationService = GetService<INavigationService>();
            var navigationArguments = new Dictionary<string, object>();

            if (parameter.HorizontalVelocity < 0)
            {
                if (PhotoRepository.Photos.Count > 0)
                {
                    navigationArguments.Add("id", 0);
                    navigationService.Navigate<PhotoViewModel>(navigationArguments);
                }
            }
        }

        /// <summary>
        /// Gets the TakePhoto command.
        /// </summary>
        public Command TakePhoto { get; private set; }

        /// <summary>
        /// Method to check whether the TakePhoto command can be executed.
        /// </summary>
        /// <returns><c>true</c> if the command can be executed; otherwise <c>false</c></returns>
        private bool OnTakePhotoCanExecute()
        {
            return !IsTakingPhoto;
        }

        /// <summary>
        /// Method to invoke when the TakePhoto command is executed.
        /// </summary>
        private void OnTakePhotoExecute()
        {
            IsTakingPhoto = true;

            CameraService.CaptureImage();
        }

        /// <summary>
        /// Gets the View command.
        /// </summary>
        public Command View { get; private set; }

        /// <summary>
        /// Method to check whether the View command can be executed.
        /// </summary>
        /// <returns><c>true</c> if the command can be executed; otherwise <c>false</c></returns>
        private bool OnViewCanExecute()
        {
            return (PhotoRepository.Photos.Count > 0);
        }

        /// <summary>
        /// Method to invoke when the View command is executed.
        /// </summary>
        private void OnViewExecute()
        {
            var navigationService = GetService<INavigationService>();
            var navigationArguments = new Dictionary<string, object>();
            navigationArguments.Add("id", 0);
            navigationService.Navigate<PhotoViewModel>(navigationArguments);
        }

        /// <summary>
        /// Gets the About command.
        /// </summary>
        public Command About { get; private set; }

        /// <summary>
        /// Method to invoke when the About command is executed.
        /// </summary>
        private void OnAboutExecute()
        {
            var navigationService = GetService<INavigationService>();
            navigationService.Navigate<AboutViewModel>();
        }
        #endregion

        #region Methods
        /// <summary>
        /// Called when the <see cref="ICameraService.CaptureThumbnailAvailable"/> event is raised.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Catel.MVVM.Services.ContentReadyEventArgs"/> instance containing the event data.</param>
        private void OnCameraServiceCaptureThumbnailAvailable(object sender, ContentReadyEventArgs e)
        {
            BitmapImage bitmap = new BitmapImage();
            bitmap.SetSource(e.ImageStream);
            CurrentPhoto = bitmap;
        }

        /// <summary>
        /// Called when the <see cref="ICameraService.CaptureImageAvailable"/> event is raised.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Catel.MVVM.Services.ContentReadyEventArgs"/> instance containing the event data.</param>
        private void OnCameraServiceCaptureImageAvailable(object sender, ContentReadyEventArgs e)
        {
            CameraService.Stop();

            BitmapImage bitmap = new BitmapImage();
            bitmap.SetSource(e.ImageStream);

            PhotoRepository.Photos.Add(bitmap);

            IsTakingPhoto = false;

            var navigationService = GetService<INavigationService>();
            var navigationArguments = new Dictionary<string, object>();
            navigationArguments.Add("id", PhotoRepository.Photos.IndexOf(bitmap));
            navigationService.Navigate<PhotoViewModel>(navigationArguments);
        }

        /// <summary>
        /// Closes this instance. Always called after the <see cref="M:Catel.MVVM.ViewModelBaseWithoutServices.Cancel"/> of <see cref="M:Catel.MVVM.ViewModelBaseWithoutServices.Save"/> method.
        /// </summary>
        /// <remarks>
        /// When implementing this method in a base class, make sure to call the base, otherwise <see cref="P:Catel.MVVM.ViewModelBaseWithoutServices.IsClosed"/> will
        /// not be set to true.
        /// </remarks>
        protected override void Close()
        {
            CameraService.Stop();
            CameraService.CaptureThumbnailAvailable -= OnCameraServiceCaptureThumbnailAvailable;
            CameraService.CaptureImageAvailable -= OnCameraServiceCaptureImageAvailable;
        }
        #endregion
    }
}